var AgentClient = function(option) {

	var version = '3.5.20200816';

	var self = this;
	var socket = null;
	var acwTimer = null;
	var loginTimer = null;

	var actionType = 0;
	var appointType = 0;

	var isOnline = false;
	//单步类型  1转接 2会议
	var  singleStepType = null;

	var agent = {};
	var config = {
		configUrl: ''
	};

	var agentDefault = {
		agentId : '',
		agentDn : '',
		agentName : '',
		agentType : 5,
		skillDesc : '',
		entId : '',
		tenantDesc : '',
		acdAgentId : '',
		acdAgentGroup : '',
		acdAgentPassword : '',
		camponFlag: 0,
		status: -1,
		agentIp: '',
		phoneIp: '',
		loginId: '',
		loginTime: '',
		logoutTime: '',
		sessionId: '',
		isLogin: false
	};

	var configDefault = {
		configUrl : '',		// 配置服务地址
		proxyUrl : '',		// 代理服务地址
		answerUrl : '',		// 话机应答地址
		hangupUrl : '',		// 话机挂机地址
		setautoUrl : '',	// 自动应答地址
		autoLogin : true,	// 是否自动签入
		forceLogin : true,	// 是否强制签入
		loginReady : false,	// 签入自动空闲
		autoAnswer : false,	// 是否自动应答
		autoReady : 1,		// 自动空闲, 1:事后整理(默认), 2:自动空闲, 3:自动忙碌
		acwTime : 0,		// 事后整理时长(毫秒)
		answerTime : 2000,	// 自动应答延时(毫秒)
		transferFlag : 1,	// 转接标志, 1:通话转(默认), 2:振铃转
		dialPrefix : '0',	// 外呼前缀
		dialCaller : '',	// 外显号码
		autoCaller : false,	// 自动外显
		agentPrefix: '',	// 坐席出局号（用于电话坐席）
		agentCaller: '',	// 坐席外显号（用于电话坐席）
		ivrDn : '6000',		// IVR号码
		owReasons : [],		// 小休原因
		debug : false,		// 启用调试
		cryptoCipher: ''	//默认不启用加密
	};

	setOption(option);

	function getOption(field) {
		if (field == undefined) {
			return {
				agentId : agent.agentId,
				agentDn : agent.agentDn,
				agentName : agent.agentName,
				agentType : agent.agentType,
				skillDesc : agent.skillDesc,
				entId : agent.entId,
				tenantDesc : agent.tenantDesc,
				acdAgentId : agent.acdAgentId,
				acdAgentGroup : agent.acdAgentGroup,
				acdAgentPassword : agent.acdAgentPassword,
				camponFlag: agent.camponFlag,
				status: agent.status,
				agentIp: agent.agentIp,
				phoneIp: agent.phoneIp,
				sessionId : agent.sessionId,
				isLogin : agent.isLogin,
				proxyUrl : config.proxyUrl,
				answerUrl : config.answerUrl,
				hangupUrl : config.hangupUrl,
				setautoUrl : config.setautoUrl,
				autoLogin : config.autoLogin,
				forceLogin : config.forceLogin,
				loginReady : config.loginReady,
				autoAnswer : config.autoAnswer,
				autoReady : config.autoReady,
				acwTime : config.acwTime,
				answerTime : config.answerTime,
				transferFlag : config.transferFlag,
				dialPrefix : config.dialPrefix,
				dialCaller : config.dialCaller,
				autoCaller : config.autoCaller,
				agentPrefix : config.agentPrefix,
				agentCaller : config.agentCaller,
				ivrDn : config.ivrDn,
				owReasons : config.owReasons,
				debug : config.debug,
				cryptoCipher: config.cryptoCipher
			};
		} else {
			var value = agent[field];

			if (value == undefined) {
				value = config[field];
			}

			return value;
		}
	}

	function setOption(option) {

		option = option || {};

		if (!agent.isLogin) {
			if (option.hasOwnProperty('agentId')) {
				agent.agentId = (option.agentId || '') + '';
			}

			if (option.hasOwnProperty('agentDn')) {
				agent.agentDn = (option.agentDn || '') + '';
			}

			if (option.hasOwnProperty('agentName')) {
				agent.agentName = (option.agentName || '') + '';
			}

			if (option.hasOwnProperty('agentType')) {
				agent.agentType = option.agentType;
			}

			if (option.hasOwnProperty('skillDesc')) {
				agent.skillDesc = (option.skillDesc || '') + '';
			}
			if (option.hasOwnProperty('entId')) {
				agent.entId = (option.entId || '') + '';
			}

			if (option.hasOwnProperty('tenantDesc')) {
				agent.tenantDesc = (option.tenantDesc || '') + '';
			}

			if (option.hasOwnProperty('acdAgentId')) {
				agent.acdAgentId = (option.acdAgentId || '') + '';
			}

			if (option.hasOwnProperty('acdAgentGroup')) {
				agent.acdAgentGroup = (option.acdAgentGroup || '') + '';
			}

			if (option.hasOwnProperty('acdAgentPassword')) {
				agent.acdAgentPassword = (option.acdAgentPassword || '') + '';
			}

			if (option.hasOwnProperty('camponFlag')) {
				agent.camponFlag = option.camponFlag;
			}

			if (option.hasOwnProperty('sessionId')) {
				agent.sessionId = (option.sessionId || '') + '';
			}

			if (!agent.sessionId) {
				agent.sessionId = getCookie('sid-' + agent.agentId);
			}
		}

		if (option.hasOwnProperty('configUrl')) {
			config.configUrl = (option.configUrl || '') + '';
		}

		if (option.hasOwnProperty('proxyUrl')) {
			config.proxyUrl = (option.proxyUrl || '') + '';
		}

		if (option.hasOwnProperty('answerUrl')) {
			config.answerUrl = (option.answerUrl || '') + '';
		}

		if (option.hasOwnProperty('hangupUrl')) {
			config.hangupUrl = (option.hangupUrl || '') + '';
		}

		if (option.hasOwnProperty('setautoUrl')) {
			config.setautoUrl = (option.setautoUrl || '') + '';
		}

		if (option.hasOwnProperty('autoLogin')) {
			config.autoLogin = option.autoLogin;
		}

		if (option.hasOwnProperty('forceLogin')) {
			config.forceLogin = option.forceLogin;
		}

		if (option.hasOwnProperty('loginReady')) {
			config.loginReady = option.loginReady;
		}

		if (option.hasOwnProperty('autoAnswer')) {
			config.autoAnswer = option.autoAnswer;
		}

		if (option.hasOwnProperty('autoReady')) {
			config.autoReady = option.autoReady;
		}

		if (option.hasOwnProperty('acwTime')) {
			config.acwTime = option.acwTime;
		}

		if (option.hasOwnProperty('answerTime')) {
			config.answerTime = option.answerTime;
		}

		if (option.hasOwnProperty('transferFlag')) {
			config.transferFlag = option.transferFlag;
		}

		if (option.hasOwnProperty('dialPrefix')) {
			config.dialPrefix = (option.dialPrefix || '') + '';
		}

		if (option.hasOwnProperty('dialCaller')) {
			config.dialCaller = (option.dialCaller || '') + '';
		}

		if (option.hasOwnProperty('autoCaller')) {
			config.autoCaller = option.autoCaller;
		}

		if (option.hasOwnProperty('agentPrefix')) {
			config.agentPrefix = (option.agentPrefix || '') + '';
		}

		if (option.hasOwnProperty('agentCaller')) {
			config.agentCaller = (option.agentCaller || '') + '';
		}

		if (option.hasOwnProperty('ivrDn')) {
			config.ivrDn = (option.ivrDn || '') + '';
		}

		if (option.hasOwnProperty('owReasons')) {
			config.owReasons = option.owReasons;
		}

		if (option.hasOwnProperty('debug')) {
			config.debug = option.debug;
		}

		if (option.hasOwnProperty('cryptoCipher')) {
			config.cryptoCipher = (option.cryptoCipher || '') + '';
		}

		return true;
	}

	function setServerOption(option) {

		option = option || {};

		if (!agent.isLogin) {
			if (option.hasOwnProperty('agentId')) {
				if (agent.agentId == undefined) {
					agent.agentId = (option.agentId || '') + '';
				}
			}

			if (option.hasOwnProperty('agentDn')) {
				if (agent.agentDn == undefined) {
					agent.agentDn = (option.agentDn || '') + '';
				}
			}

			if (option.hasOwnProperty('agentName')) {
				if (agent.agentName == undefined) {
					agent.agentName = (option.agentName || '') + '';
				}
			}

			if (option.hasOwnProperty('agentType')) {
				if (agent.agentType == undefined) {
					agent.agentType = option.agentType;
				}
			}

			if (option.hasOwnProperty('skillDesc')) {
				if (agent.skillDesc == undefined) {
					agent.skillDesc = (option.skillDesc || '') + '';
				}
			}
			if (option.hasOwnProperty('entId')) {
				if (agent.entId == undefined) {
					agent.entId = (option.entId || '') + '';
				}
			}

			if (option.hasOwnProperty('tenantDesc')) {
				if (agent.tenantDesc == undefined) {
					agent.tenantDesc = (option.tenantDesc || '') + '';
				}
			}

			if (option.hasOwnProperty('acdAgentId')) {
				if (agent.acdAgentId == undefined) {
					agent.acdAgentId = (option.acdAgentId || '') + '';
				}
			}

			if (option.hasOwnProperty('acdAgentGroup')) {
				if (agent.acdAgentGroup == undefined) {
					agent.acdAgentGroup = (option.acdAgentGroup || '') + '';
				}
			}

			if (option.hasOwnProperty('acdAgentPassword')) {
				if (agent.acdAgentPassword == undefined) {
					agent.acdAgentPassword = (option.acdAgentPassword || '') + '';
				}
			}

			if (option.hasOwnProperty('camponFlag')) {
				if (agent.camponFlag == undefined) {
					agent.camponFlag = option.camponFlag;
				}
			}
		}

		if (option.hasOwnProperty('proxyUrl')) {
			if (config.proxyUrl == undefined) {
				config.proxyUrl = (option.proxyUrl || '') + '';
			}
		}

		if (option.hasOwnProperty('answerUrl')) {
			if (config.answerUrl == undefined) {
				config.answerUrl = (option.answerUrl || '') + '';
			}
		}

		if (option.hasOwnProperty('hangupUrl')) {
			if (config.hangupUrl == undefined) {
				config.hangupUrl = (option.hangupUrl || '') + '';
			}
		}

		if (option.hasOwnProperty('setautoUrl')) {
			if (config.setautoUrl == undefined) {
				config.setautoUrl = (option.setautoUrl || '') + '';
			}
		}

		if (option.hasOwnProperty('autoLogin')) {
			if (config.autoLogin == undefined) {
				config.autoLogin = option.autoLogin;
			}
		}

		if (option.hasOwnProperty('forceLogin')) {
			if (config.forceLogin == undefined) {
				config.forceLogin = option.forceLogin;
			}
		}

		if (option.hasOwnProperty('loginReady')) {
			if (config.loginReady == undefined) {
				config.loginReady = option.loginReady;
			}
		}

		if (option.hasOwnProperty('autoAnswer')) {
			if (config.autoAnswer == undefined) {
				config.autoAnswer = option.autoAnswer;
			}
		}

		if (option.hasOwnProperty('autoReady')) {
			if (config.autoReady == undefined) {
				config.autoReady = option.autoReady;
			}
		}

		if (option.hasOwnProperty('acwTime')) {
			if (config.acwTime == undefined) {
				config.acwTime = option.acwTime;
			}
		}

		if (option.hasOwnProperty('answerTime')) {
			if (config.answerTime == undefined) {
				config.answerTime = option.answerTime;
			}
		}

		if (option.hasOwnProperty('transferFlag')) {
			if (config.transferFlag == undefined) {
				config.transferFlag = option.transferFlag;
			}
		}

		if (option.hasOwnProperty('dialPrefix')) {
			if (config.dialPrefix == undefined) {
				config.dialPrefix = (option.dialPrefix || '') + '';
			}
		}

		if (option.hasOwnProperty('dialCaller')) {
			if (config.dialCaller == undefined) {
				config.dialCaller = (option.dialCaller || '') + '';
			}
		}

		if (option.hasOwnProperty('autoCaller')) {
			if (config.autoCaller == undefined) {
				config.autoCaller = option.autoCaller;
			}
		}

		if (option.hasOwnProperty('agentPrefix')) {
			if (config.agentPrefix == undefined) {
				config.agentPrefix = (option.agentPrefix || '') + '';
			}
		}

		if (option.hasOwnProperty('agentCaller')) {
			if (config.agentCaller == undefined) {
				config.agentCaller = (option.agentCaller || '') + '';
			}
		}

		if (option.hasOwnProperty('ivrDn')) {
			if (config.ivrDn == undefined) {
				config.ivrDn = (option.ivrDn || '') + '';
			}
		}

		if (option.hasOwnProperty('owReasons')) {
			if (config.owReasons == undefined) {
				config.owReasons = option.owReasons;
			}
		}

		if (option.hasOwnProperty('debug')) {
			if (config.debug == undefined) {
				config.debug = option.debug;
			}
		}

		if (option.hasOwnProperty('cryptoCipher')) {
			if (config.cryptoCipher == undefined) {
				config.cryptoCipher = option.cryptoCipher;
			}
		}
	}

	function setDefault() {

		for (var key in agentDefault) {
			if (agent[key] == undefined) {
				agent[key] = agentDefault[key];
			}
		}

		for (var key in configDefault) {
			if (config[key] == undefined) {
				config[key] = configDefault[key];
			}
		}
	}

	function start() {

		if (config.configUrl) {

			var url = config.configUrl + '/agent?agentId=' + agent.agentId;

			httpGet(url, function(response) {

				log('request: ' + url, 'response: ' + response);

				if (response) {
					setServerOption(JSON.parse(response));
				}

				setDefault();
				connect();
			});
		} else {
			setDefault();
			connect();
		}
	}

	function connect() {

		if (!socket) {

			log('start agent: ', agent);
			log('start config: ', config);

			socket = io.connect(config.proxyUrl, {forceNew: true});

			socket.on('connect', function() {
				onMessage(createAgentClientOnLineEvent());
				//getEvent(createAgentClientOnLineEvent());
			});

			socket.on('connect_error', function(data) {
				var errorCode = '1343225931';
				var errorDesc = 'Err_IContact_' + (data ? data.type || 'TransportError' : 'TransportError');
				onMessage(createUniversalFailureEvent(errorCode, errorDesc));
				//getEvent(createUniversalFailureEvent(errorCode, errorDesc));
			});

			socket.on('disconnect', function() {
				onMessage(createAgentClientOffLineEvent());
				//getEvent(createAgentClientOffLineEvent());
			});

			socket.on('message', function(message) {
				//内部流转
				onMessage(message);
			});
		}
	}

	function getEvent(event){

		if (isFunction(self.onEvent)) {

			//agent.status = currStatus;
			self.onEvent(event);
			switch (event.MessageID) {
				case Evt_AgentStatus_Consulted:
					onSingleStep(event);
					break;
			}

		}
	}

	function stop() {

		if (agent.isLogin) {
			logout();
		}

		setTimeout(function() {
			if (socket && isOnline) {
				socket.close();
			}
		}, 100);
	}

	function login(isForce) {

		var agentPrivateData = {};

		if (agent.agentType == AGENT_TYPE_PHONE) {
			agentPrivateData = {
				TAC: config.agentPrefix || config.dialPrefix || '',
				CALLERID: config.agentCaller || config.dialCaller || ''
			};
		}

		agentPrivateData.cryptoCipher = config.cryptoCipher || '';		
		return sendCommand(createLoginCommand(agent, JSON.stringify(agentPrivateData)));
	}

	function logout() {
		if (agent.isLogin) {
			return sendCommand(createLogoutCommand(agent.agentId));
		}
		return false;
	}

	function notReady() {		
		if (agent.isLogin) {
			return sendCommand(createNotReadyCommand(agent.agentId));
		}
		return false;
	}

	function ready() {
		if (agent.isLogin) {
			return sendCommand(createReadyCommand(agent.agentId));
		}
		return false;
	}

	function otherWork(reason) {
		if (agent.isLogin) {
			return sendCommand(createOtherWorkCommand(agent.agentId, reason));
		}
		return false;
	}

	function appoint(_appointType) {
		appointType = _appointType;
		return true;
	}

	function forceLogin() {

		forceLogout(agent.agentId);

		setTimeout(function() {
			login();
		}, 100);
	}

	function makeCall(destType, destDesc, callerId) {

		// // 是否呼叫外线
		// if (destType === DEST_TYPE_EXTERDN) {
		//
		// 	// 如果未传外呼前缀参数，则使用初始设置的外呼前缀
		// 	if (dialPrefix === null || dialPrefix === undefined) {
		// 		dialPrefix = config.dialPrefix;
		// 	}
		//
		// 	// 如果未传外显号码，并且设置了自动外显、配置服务地址和外呼前缀，则调用自动外显接口获取外显号码发起外呼，否则使用初始外显号码
		// 	if (callerId === null || callerId === undefined || callerId === '') {
		// 		if (config.autoCaller && config.configUrl && dialPrefix) {
		// 			return makeAutoCaller(destType, destDesc, callerId, tenantId, dialPrefix);
		// 		} else {
		// 			callerId = config.dialCaller;
		// 		}
		// 	}
		// } else {
		// 	callerId = '';
		// }

		return sendCommand(createMakeCallCommand(agent.agentId, destType, destDesc, callerId));
	}

	function makeAutoCaller(destType, destDesc, callerId, tenantId, dialPrefix) {

		var callerUrl = config.configUrl + '/caller?prefixDn=' + dialPrefix;

		httpGet(callerUrl, function(responseText) {

			log('request: ' + callerUrl, 'response: ' + responseText);

			try {
				var response = JSON.parse(responseText);

				if (response && response.callerDn) {
					callerId = response.callerDn;
				}
			} catch (error) {
				log('parse response: ', responseText, 'error: ', error);
			}

			if (callerId === null || callerId === undefined || callerId === '') {
				callerId = config.dialCaller;
			}

			sendCommand(createMakeCallCommand(agent.agentId, destType, destDesc, callerId, tenantId, dialPrefix));

		}, function() {

			if (callerId === null || callerId === undefined || callerId === '') {
				callerId = config.dialCaller;
			}

			sendCommand(createMakeCallCommand(agent.agentId, destType, destDesc, callerId, tenantId, dialPrefix));
		});

		return true;
	}
	
	function doAnswer() {
		if (agent.isLogin) {
			httpGet(buildHttpUrl(config.answerUrl));
			return sendCommand(createAnswerCallCommand(agent.agentId, agent.agentDn));
		}
		return false;
	}

	function autoAnswerCall() {
		if (config.autoAnswer) {
			setTimeout(function() {
				doAnswer();
			}, config.answerTime);
		}
	}

	function doHangup() {
		if (agent.isLogin) {
			httpGet(buildHttpUrl(config.hangupUrl));
			return sendCommand(createHangupCallCommand(agent.agentId, agent.agentDn));
		}
		return false;
	}

	function clearCall() {
		if (agent.isLogin) {
			return sendCommand(createClearCallCommand(agent.agentId, agent.agentDn));
		}
		return false;
	}

	function doHold() {
		if (agent.isLogin) {
			return sendCommand(createHoldCallCommand(agent.agentId, agent.acdAgentPassword));
		}
		return false;
	}

	function doRetrive() {
		if (agent.isLogin) {
			actionType = APPOINT_TYPE_NULL;
			return sendCommand(createRetrieveCallCommand(agent.agentId, agent.acdAgentPassword));
		}
		return false;
	}

	function doConsulting(consultType, destDesc, sessionId) {

		if (agent.isLogin) {


			return sendCommand(createConsultCallCommand(agent.agentId, agent.acdAgentPassword, sessionId,consultType, destDesc));
		}

		return false;
	}

	function reconnectCall() {
		if (agent.isLogin) {
			actionType = ACTION_TYPE_NULL;
			return sendCommand(createReconnectCallCommand(agent.agentId, agent.agentDn));
		}
		return false;
	}

	function forwardCall() {
		if (agent.isLogin) {
			actionType = ACTION_TYPE_NULL;
			return sendCommand(createTransferCallCommand(agent.agentId, agent.agentDn));
		}
		return false;
	}

	function doMeeting() {
		if (agent.isLogin) {
			actionType = ACTION_TYPE_NULL;
			return sendCommand(createConferenceCallCommand(agent.agentId, agent.acdAgentPassword));
		}
		return false;
	}

	function deflectCall(destType, destDesc) {
		if (agent.isLogin) {
			return sendCommand(createDeflectCallCommand(agent.agentId, agent.agentDn, destType, destDesc));
		}
		return false;
	}

	function pickupCall(destType, destDesc) {
		if (agent.isLogin) {
			return sendCommand(createPickupCallCommand(agent.agentId, agent.agentDn, destType, destDesc));
		}
		return false;
	}

	function doAdressShift() {

		if (agent.isLogin) {

			sendCommand(createTransferCallCommand(agent.agentId, agent.acdAgentPassword));
		}

		return false;
	}

	function doTransferIVR(callData) {


		singleStepTransfer(callData);
	}

	//destType (1开始 2结束)
	function whisper(sessionId,destType){

		if (agent.isLogin) {
			return sendCommand(createWhisperCommand(agent.agentId, agent.acdAgentPassword, sessionId,destType));
		}
		return false;
	}

	function doAssistService(dataKey, dataValue){

		if (agent.isLogin) {
			return sendCommand(createAssistServiceCommand(agent.agentId, agent.acdAgentPassword, dataKey, dataValue));
		}
		return false;
	}


	function sendDTMF(DTMF) {
		if (agent.isLogin) {
			return sendCommand(createSendDTMFCommand(agent.agentId, DTMF));
		}
		return false;
	}

	function singleStepTransfer(destDesc) {
		if (agent.isLogin) {
			return sendCommand(createSingleStepTransferCommand(agent.entId, agent.agentId,agent.acdAgentPassword, destDesc));
		}
		return false;
	}

	function forceNotReady(agentId) {
		if (agent.isLogin) {
			return sendCommand(createForceNotReadyCommand(agent.agentId, agentId));
		}
		return false;
	}

	function forceReady(agentId) {
		if (agent.isLogin) {
			return sendCommand(createForceReadyCommand(agent.agentId, agentId));
		}
		return false;
	}

	function forceLogout(agentId) {
		if (agent.isLogin) {
			return sendCommand(createForceLogoutCommand(agent.agentId, agentId));
		} else {
			return sendCommand(createForceLogoutCommand('', agentId));
		}
	}

	function doListen(destAgentId) {
		if (agent.isLogin) {
			//httpGet(buildHttpUrl(config.setautoUrl));
			return sendCommand(createListenCommand(agent.agentId, agent.acdAgentPassword, destAgentId));
		}
		return false;
	}

	function doInsert() {
		if (agent.isLogin) {
			//httpGet(buildHttpUrl(config.setautoUrl));
			return sendCommand(createForceInsertCommand(agent.agentId));
		}
		return false;
	}

	function interceptCall(destType, destDesc) {
		if (agent.isLogin) {
			httpGet(buildHttpUrl(config.setautoUrl));
			return sendCommand(createInterceptCommand(agent.agentId, agent.agentDn, destType, destDesc));
		}
		return false;
	}

	function doDrop() {
		if (agent.isLogin) {
			return sendCommand(createForceHangupCommand(agent.agentId));
		}
		return false;
	}

	function setCallData(sessionId,dataKey, dataValue) {
		if (agent.isLogin) {
			return sendCommand(createSetCallDataCommand(agent.agentId,sessionId, dataKey, dataValue));
		}
		return false;
	}

	function getCallData(sessionId,dataKey) {

		if (agent.isLogin && dataKey && sessionId) {

/*			var timeout = true;
			var token = PubSub.subscribe(Resp_IContact_GetCallData, function(topic, response) {

				if (response && response.Key_IContact_CallDataKey == dataKey) {

					timeout = false;
					PubSub.unsubscribe(token);

					if (isFunction(success)) {
						success(response.Key_IContact_CallDataValue);
					}
				}
			});

			setTimeout(function() {
				if (timeout) {
					PubSub.unsubscribe(token);
					if (isFunction(error)) {
						error();
					}
				}
			}, 2000);*/

			return sendCommand(createGetCallDataCommand(agent.agentId,sessionId, dataKey));
		}

		return false;
	}

	function getAgentState(agentId) {
		if (agent.isLogin) {
			return sendCommand(createGetAgentStateCommand(agent.entId,agentId,agent.acdAgentPassword,''));
		}

        return false;
    }

	function startRecord(fileName) {
		if (agent.isLogin) {
			return sendCommand(createStartRecordCommand(agent.agentId, agent.agentDn, fileName));
		}
		return false;
	}

	function stopRecord() {
		if (agent.isLogin) {
			return sendCommand(createStopRecordCommand(agent.agentId, agent.agentDn));
		}
		return false;
	}

	function routeSelect(contactId, destType, destDesc) {
		if (agent.isLogin) {
			return sendCommand(createRouteSelectCommand(agent.agentId, agent.agentDn, contactId, destType, destDesc));
		}
		return false;
	}

	function startMute() {
		if (agent.isLogin) {
			return sendCommand(createStartMuteCommand(agent.agentId, agent.agentDn));
		}
	}

	function stopMute() {
		if (agent.isLogin) {
			return sendCommand(createStopMuteCommand(agent.agentId, agent.agentDn));
		}
	}

	function setSipHead(headKey, headValue) {
		if (agent.isLogin) {
			return sendCommand(createSetSipHeadCommand(agent.agentId, agent.agentDn, headKey, headValue));
		}
	}

	function getSipHead(headKey, success, error) {

		if (agent.isLogin && headKey) {

			var timeout = true;
			var token = PubSub.subscribe(Evt_IContact_GetSipHeadEnd, function(topic, event) {

				if (event && event.Key_IContact_HeadKey == headKey) {

					timeout = false;
					PubSub.unsubscribe(token);

					if (isFunction(success)) {
						success(event.Key_IContact_HeadValue);
					}
				}
			});

			setTimeout(function() {
				if (timeout) {
					PubSub.unsubscribe(token);
					if (isFunction(error)) {
						error();
					}
				}
			}, 2000);

			return sendCommand(createGetSipHeadCommand(agent.agentId, agent.agentDn, headKey));
		}

		return false;
	}

	function  getAgentListData(){
		if (agent.isLogin) {
			return sendCommand(createAgentListCommand(agent.entId));
		}
		return false;
	}


	function  getAgentDialCaller(){
		if (agent.isLogin) {
			return sendCommand(createAgentDialCallerCommand(agent.entId,agent.agentId));
		}
		return false;
	}



	function execCommand(appName, appData, privateData) {
		if (agent.isLogin) {
			return sendCommand(createExecCommand(agent.agentId, agent.agentDn, appName, appData, privateData));
		}
	}

	function agentStateReset(destDesc){
		if (agent.isLogin) {
			return sendCommand(createAgentStateResetCommand(agent.agentId,destDesc));
		}

	}

	function sendCommand(command) {

		if (isOnline) {

			if (agent.sessionId) {
				command.Key_IContact_SessionID = agent.sessionId;
			}

			debug('send command:', command);

			socket.emit('command', command);

			return true;
		}
		return false;
	}

	function createLoginCommand(agent, agentPrivateData) {
		return {
			MessageID : Cmd_IContact_AgentLogin,
			Key_IContact_AgentID : agent.agentId,
			Key_IContact_AgentDN : agent.agentDn,
			Key_IContact_AgentName : agent.agentName,
			Key_IContact_AgentType : agent.agentType,
			Key_IContact_SkillDesc : agent.skillDesc,
			Key_IContact_TenantDesc : agent.tenantDesc,
			Key_IContact_ACDAgentID : agent.acdAgentId,
			Key_IContact_ACDAgentGroup : agent.acdAgentGroup,
			Key_IContact_ACDAgentPassword : agent.acdAgentPassword,
			Key_IContact_CampOn : agent.camponFlag,
			Key_IContact_AgentPrivateData : agentPrivateData + ''
		};
	}

	function createLogoutCommand(agentId) {
		return {
			MessageID : Cmd_IContact_AgentLogout,
			Key_IContact_AgentID : agentId + ''
		};
	}

	function createNotReadyCommand(agentId) {
		return {
			MessageID : Cmd_IContact_AgentNotReady,
			Key_IContact_AgentID : agentId + ''
		};
	}

	function createReadyCommand(agentId) {
		return {
			MessageID : Cmd_IContact_AgentReady,
			Key_IContact_AgentID : agentId + ''
		};
	}

	function createOtherWorkCommand(agentId, reason) {
		return {
			MessageID : Cmd_IContact_AgentOtherWork,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_Reason : parseInt(reason)
		};
	}

	function createMakeCallCommand(agentId, destType, destDesc, callingParty) {
		return {
			MessageID : Cmd_IContact_MakeCall,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DestType : parseInt(destType),
			Key_IContact_DestDesc : (destDesc || '') + '',
			Key_IContact_CallingParty : (callingParty || '') + '',
			Key_IContact_TenantID : '',
			Key_IContact_DialPrefix :'',
			Key_IContact_ACDAgentPassword : agent.acdAgentPassword
		};
	}

	function createAnswerCallCommand(agentId, agentDn) {
		return {
			MessageID : Cmd_IContact_AnswerCall,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + ''
		};
	}

	function createHangupCallCommand(agentId, agentDn) {
		return {
			MessageID : Cmd_IContact_HangupCall,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + ''
		};
	}

	function createClearCallCommand(agentId, agentDn) {
		return {
			MessageID : Cmd_IContact_ClearCall,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + ''
		};
	}

	function createHoldCallCommand(agentId, agentPwd) {
		return {
			MessageID : Cmd_IContact_HoldCall,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_ACDAgentPassword : agentPwd + ''
		};
	}

	function createRetrieveCallCommand(agentId, agentPwd) {
		return {
			MessageID : Cmd_IContact_RetrieveCall,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_ACDAgentPassword : agentPwd + ''
		};
	}

	function createTransferCallCommand(agentId, agentPwd) {
		return {
			MessageID : Cmd_IContact_TransferCall,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_ACDAgentPassword : agentPwd + ''
		};
	}

	function createConferenceCallCommand(agentId, agentPwd) {
		return {
			MessageID : Cmd_IContact_ConferenceCall,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_ACDAgentPassword : agentPwd + ''
		};
	}

	function createConsultCallCommand(agentId, agentPwd, sessionId, consultType, destDesc) {
		return {
			MessageID : Cmd_IContact_ConsultCall,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_ACDAgentPassword : agentPwd + '',
			Key_IContact_SessionID : sessionId,
			Key_IContact_ConsultType : parseInt(consultType),
			Key_IContact_ConsultNumber : destDesc + '',
			Key_IContact_TimeStamp : (new Date()).valueOf()+ ''
		};
	}

	function createWhisperCommand(agentId,agentPwd,sessionId,destType) {
		return {
			MessageID : Cmd_IContact_Whisper,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_ACDAgentPassword : agentPwd + '',
			Key_IContact_SessionID : sessionId + '',
			Key_IContact_IsWhisperApi: destType + ''
		};
	}
	function createAssistServiceCommand(agentId,agentPwd,dataKey, dataValue) {
		return {
			MessageID : Cmd_IContact_AssistService,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_ACDAgentPassword : agentPwd + '',
			Key_IContact_ServiceDataKey : dataKey + '',
			Key_IContact_ServiceDataValue: dataValue + ''
		};
	}

	function createReconnectCallCommand(agentId, agentDn) {
		return {
			MessageID : Cmd_IContact_ConsultReconnect,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + ''
		};
	}

	function createDeflectCallCommand(agentId, agentDn, destType, destDesc) {
		return {
			MessageID : Cmd_IContact_DeflectCall,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + '',
			Key_IContact_DestType : parseInt(destType),
			Key_IContact_DestDesc : destDesc + ''
		};
	}

	function createPickupCallCommand(agentId, agentDn, destType, destDesc) {
		return {
			MessageID : Cmd_IContact_PickupCall,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + '',
			Key_IContact_DestType : parseInt(destType),
			Key_IContact_DestDesc : destDesc + ''
		};
	}

	function createSendDTMFCommand(agentId, agentDn, DTMF) {
		console.log('agent-client.js----发送命令TMF', agentId, agentDn, DTMF)
		return {
			MessageID : Cmd_IContact_SendDTMF,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DTMF : DTMF + ''
		};
	}

	function createSingleStepTransferCommand(entId, agentId,agentPwd, destDesc) {
		return {
			MessageID : Cmd_IContact_TransferIVR,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_ACDAgentPassword : agentPwd + '',
			entId : entId + '',
			Key_IContact_CallData : destDesc + ''
		};
	}

	function createForceLogoutCommand(agentId, destAgentId) {
		return {
			MessageID : Cmd_IContact_ForceAgentLogout,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DestAgentID : destAgentId + ''
		};
	}

	function createForceReadyCommand(agentId, destAgentId) {
		return {
			MessageID : Cmd_IContact_ForceAgentReady,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DestAgentID : destAgentId + ''
		};
	}

	function createForceNotReadyCommand(agentId, destAgentId) {
		return {
			MessageID : Cmd_IContact_ForceAgentNotReady,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DestAgentID : destAgentId + ''
		};
	}

	function createListenCommand(agentId, agentPwd, destAgentId) {
		return {
			MessageID : Cmd_IContact_Listen,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_ACDAgentPassword : agentPwd + '',
			Key_IContact_DestAgentID : destAgentId+ ''
		};
	}

	function createForceInsertCommand(agentId) {
		return {
			MessageID : Cmd_IContact_ForceInsert,
			Key_IContact_AgentID : agentId + ''
		};
	}

	function createInterceptCommand(agentId, agentDn, destType, destDesc) {
		return {
			MessageID : Cmd_IContact_Intercept,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + '',
			Key_IContact_DestType : parseInt(destType),
			Key_IContact_DestDesc : destDesc + ''
		};
	}

	function createForceHangupCommand(agentId) {
		return {
			MessageID : Cmd_IContact_ForceHangup,
			Key_IContact_AgentID : agentId + ''
		};
	}

	function createSetCallDataCommand(agentId,sessionId, dataKey, dataValue) {
		return {
			MessageID : Cmd_IContact_SetAssociateData,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_SessionID : sessionId + '',
			Key_IContact_ServiceDataKey : dataKey + '',
			Key_IContact_ServiceDataValue : dataValue + ''
		};
	}

	function createGetCallDataCommand(agentId,sessionId, dataKey) {
		return {
			MessageID : Cmd_IContact_GetAssociateData,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_SessionID : sessionId + '',
			Key_IContact_ServiceDataKey : dataKey + ''
		};
	}

	function createGetAgentStateCommand(entId,agentId,agentPwd,state) {
		return {
			MessageID : Cmd_IContact_GetAgentState,
			entId : entId+ '',
			Key_IContact_AgentID : agentId + '',
			Key_IContact_ACDAgentPassword : agentPwd + '',
			Key_IContact_State : state + ''
		};
    }

	function createStartRecordCommand(agentId, agentDn, fileName) {
		return {
			MessageID : Cmd_IContact_StartRecord,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + '',
			Key_IContact_FileName : fileName + ''
		};
	}

	function createStopRecordCommand(agentId, agentDn) {
		return {
			MessageID : Cmd_IContact_StopRecord,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + ''
		};
	}

	function createRouteSelectCommand(agentId, agentDn, contactId, destType, destDesc) {
		return {
			MessageID : Cmd_IContact_AgentRouteSelect,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + '',
			Key_IContact_ContactID : contactId + '',
			Key_IContact_DestType : parseInt(destType),
			Key_IContact_DestDesc : destDesc + ''
		};
	}

	function createStartMuteCommand(agentId, agentDn) {
		return {
			MessageID : Cmd_IContact_StartMute,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + ''
		};
	}

	function createStopMuteCommand(agentId, agentDn) {
		return {
			MessageID : Cmd_IContact_StopMute,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + ''
		};
	}

	function createSetSipHeadCommand(agentId, agentDn, headKey, headValue) {
		return {
			MessageID : Cmd_IContact_SetSipHead,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + '',
			Key_IContact_HeadKey : headKey + '',
			Key_IContact_HeadValue : headValue + ''
		};
	}

	function createGetSipHeadCommand(agentId, agentDn, headKey) {
		return {
			MessageID : Cmd_IContact_GetSipHead,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_DeviceDN : agentDn + '',
			Key_IContact_HeadKey : headKey + ''
		};
	}

	function createExecCommand(agentId, agentDn, appName, appData, privateData) {
		return {
			MessageID : Cmd_IContact_ExecCommand,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_AgentDN : agentDn + '',
			Key_IContact_AppName : appName + '',
			Key_IContact_AppData : appData + '',
			Key_IContact_PrivateData : privateData + ''
		};
	}

	function createAgentClientOnLineEvent() {
		return {
			MessageID : Evt_ISystem_AgentClientOnLine,
			Key_IContact_TimeStamp : currentTime()
		}
	}

	function createAgentClientOffLineEvent() {
		return {
			MessageID : Evt_ISystem_AgentClientOffLine,
			Key_IContact_TimeStamp : currentTime()
		}
	}

	function createUniversalFailureEvent(errorCode, errorDesc) {
		return {
			MessageID: Evt_IContact_UniversalFailure,
			Key_IContact_UniversalFailureID: errorCode + '',
			Key_IContact_UniversalFailureDesc: errorDesc + '',
			Key_IContact_TimeStamp: currentTime()
		};
	}


	function createAgentListCommand(entId) {
		return {
			MessageID : Cmd_IContact_GetAgentList,
			entId : entId+ ''
		};
	}

	function createAgentDialCallerCommand(entId,agentId) {
		return {
			MessageID : Cmd_IContact_GetDialCaller,
			entId : entId+ '',
			Key_IContact_AgentID : agentId + ''
		};
	}

	function createAgentStateResetCommand(agentId,destDesc) {
		return {
			MessageID : Cmd_IContact_Reset,
			Key_IContact_AgentID : agentId + '',
			Key_IContact_AgentStatus : destDesc + ''
		};
	}

	function currentTime() {

		function f(n) {
            return n < 10 ? '0' + n : n;
        }

		function f2(n) {
			if (n < 10) return '00' + n;
            return n < 100 ? '0' + n : n;
        }

		var now = new Date();

		return now.getFullYear() + '-' +
			f(now.getMonth() + 1) + '-' +
			f(now.getDate()) + ' ' +
			f(now.getHours()) + ':' +
			f(now.getMinutes()) + ':' +
			f(now.getSeconds()) + '.' +
			f2(now.getMilliseconds());
	}

	function onMessage(message) {

		debug('received message:', message);

		if (!message || !message.MessageID) {
			return;
		}
		PubSub.publish(message.MessageID, message);
		if (message.MessageID.substring(0, 3) === 'Evt'&&message.MessageID!='Evt_null') {
			if (typeof(message.EventType) != "undefined") {
				getEvent(message)
			}else{
				onEvent(message);
			}

		} else if (message.MessageID.substring(0, 4) === 'Resp') {
			onResponse(message);
		}

	}

	function onResponse(response) {
		switch (response.MessageID) {
		case Resp_IContact_AgentLogin:
			onAgentLoginResponse(response);
			break;
		case Resp_IContact_AgentLogout:
			onAgentLogoutResponse(response);
			break;
	    case Resp_IContact_GetAgentData:
			onAgentListData(response);
			break;
	    case Resp_IContact_GetDialCaller:
			onAgentDialCallerData(response);
			break;
		case Resp_IContact_GetAgentState:
			onAgentState(response);
			break;
        case Resp_IContact_Listen:
			onAgentListenResponse(response);
        	break;
         case Resp_IContact_GetAssociateData:
         	onGetCallData(response);
         	break;
		 case Resp_IContact_Whisper:
		 	onWhisperData(response);
		 	break;
		}


	}

	function onAgentLoginResponse(response) {
		agent.isLogin = true;
		//agent.phoneIp = response.Key_IContact_PhoneAddress || '';
		if(response.code == 200){
			clearLoginTimer();
			agent.isLogin = true;
		}
		/*else{
			agent.isLogin = false;
			var event = {

				"EventType": "AgentStatus_Unkown",
				"Result": "0",
				"AgentMessage": response.msg
			}
			getEvent(event)
		}*/

	}
	function onAgentLogoutResponse(response) {		
		agent.isLogin = false;

		if(response.code == 200){
			clearLoginTimer();
		}
	}

	function onAgentListData(response) {

		if (isFunction(self.onAgentListData)) {
			self.onAgentListData(response.data);
		}

	}
	function onAgentState(response) {

		if (isFunction(self.onAgentState)) {
			var currStatus =  response.state;
			console.log(currStatus);
			agent.status = currStatus;
			self.onAgentState(currStatus);
		}

	}
	function onAgentDialCallerData(response) {

		if (isFunction(self.onAgentDialCallerData)) {
			self.onAgentDialCallerData(response.data);
		}

	}

	function onAgentListenResponse(response) {
		if(response.code != 200){

			alert(response.msg)
		}
	}
	function onGetCallData(response) {

		if (isFunction(self.onGetCallData)) {
			self.onGetCallData(response);
		}

	}
	function onWhisperData(response) {

		if (isFunction(self.onWhisperData)) {
			self.onWhisperData(response);
		}

	}



	function onEvent(event) {

		switch (event.MessageID) {

		case Evt_IContact_AgentLogin:
		case Evt_IContact_AgentNotReady:
		case Evt_IContact_AgentReady:
		case Evt_IContact_AgentLocked:
		case Evt_IContact_AgentWorking:
		case Evt_IContact_AgentAfterCallWork:
		case Evt_IContact_AgentOtherWork:
		case Evt_IContact_AgentLogout:
			onAgentEvent(event);
			break;

		case Evt_IContact_OffHook:
		case Evt_IContact_Dialing:
		case Evt_IContact_Offering:
		case Evt_IContact_Connected:
		case Evt_IContact_Released:
		case Evt_IContact_Failed:
		case Evt_IContact_Notify:
		case Evt_IContact_Held:
		case Evt_IContact_BeHeld:
		case Evt_IContact_ConsultOffHook:
		case Evt_IContact_ConsultDialing:
		case Evt_IContact_BeConsultOffering:
		case Evt_IContact_BeConsultConnected:
		case Evt_IContact_ConsultFailed:
		case Evt_IContact_Conferenced:
		case Evt_IContact_BeConferenced:
		case Evt_IContact_ForceInserted:
		case Evt_IContact_Listened:
		case Evt_IContact_Recording:
		case Evt_IContact_RecordEnd:
		case Evt_IContact_IMOffering:
		case Evt_IContact_IMConnected:
		case Evt_IContact_IMReleased:
		case Evt_IContact_DeviceMuting:
		case Evt_IContact_DeviceMuteEnd:
		case Evt_IContact_DevicePlaying:
		case Evt_IContact_DevicePlayEnd:
			onDeviceEvent(event);
			break;

		case Evt_IContact_ASFailure:
		case Evt_IContact_UniversalFailure:
			onErrorEvent(event);
			break;

		case Evt_ISystem_AgentClientOnLine:
		case Evt_ISystem_AgentClientOffLine:
		case Evt_ISystem_AgentProxyOnLine:
		case Evt_ISystem_AgentProxyOffLine:
		case Evt_IContact_AgentServiceOnLine:
		case Evt_IContact_AgentServiceOffLine:
		case Evt_IContact_LinkUp:
		case Evt_IContact_LinkDown:
			onSystemEvent(event);
			break;
		}
	}

	function onSingleStep(event) {
		if(singleStepType === 1 && event.CallDetailEvent  === 'CA_CALL_EVENT_CONSULT_CONNECTED_OP'){

			doAdressShift();

		}else if(singleStepType === 2 && event.CallDetailEvent  === 'CA_CALL_EVENT_CONSULT_CONNECTED_OP'){

			doMeeting();
		}

	}


	function onAgentEvent(event) {

		switch (event.MessageID) {

		case Evt_IContact_AgentLogin:
			onAgentLogin(event);
			fireAgentStatusChangedEvent(event);
			fireLoginEvent(event);
			break;

		case Evt_IContact_AgentNotReady:
			onAgentNotReady(event);
			fireAgentStatusChangedEvent(event);
			break;

		case Evt_IContact_AgentReady:
			fireAgentStatusChangedEvent(event);
			break;

		case Evt_IContact_AgentLocked:
			fireAgentStatusChangedEvent(event);
			break;

		case Evt_IContact_AgentWorking:
			fireAgentStatusChangedEvent(event);
			break;

		case Evt_IContact_AgentAfterCallWork:
			onAgentAfterCallWork(event);
			fireAgentStatusChangedEvent(event);
			break;

		case Evt_IContact_AgentOtherWork:
			fireAgentStatusChangedEvent(event);
			break;

		case Evt_IContact_AgentLogout:
			onAgentLogout(event);
			fireAgentStatusChangedEvent(event);
			fireLogoutEvent(event);
			break;
		}
	}

	function onDeviceEvent(event) {

		switch (event.MessageID) {

		case Evt_IContact_OffHook:
			fireDeviceStatusChangedEvent(event);
			doAnswer();
			break;

		case Evt_IContact_Dialing:
			fireDeviceStatusChangedEvent(event);
			fireDialingEvent(event);
			doAnswer();
			break;

		case Evt_IContact_Offering:
			fireDeviceStatusChangedEvent(event);
			fireOfferingEvent(event);
			autoAnswerCall();
			break;

		case Evt_IContact_Connected:
			fireDeviceStatusChangedEvent(event);
			fireConnectedEvent(event);
			break;

		case Evt_IContact_Released:
			httpGet(buildHttpUrl(config.hangupUrl));
			fireDeviceStatusChangedEvent(event);
			fireReleasedEvent(event);
			actionType = ACTION_TYPE_NULL;
			break;

		case Evt_IContact_IMOffering:
			fireDeviceStatusChangedEvent(event);
			fireIMOfferingEvent(event);
			break;

		case Evt_IContact_IMConnected:
			fireDeviceStatusChangedEvent(event);
			fireIMConnectedEvent(event);
			break;

		case Evt_IContact_IMReleased:
			fireDeviceStatusChangedEvent(event);
			fireIMReleasedEvent(event);
			break;

		case Evt_IContact_Failed:
			fireDeviceStatusChangedEvent(event);
			actionType = ACTION_TYPE_NULL;
			break;

		case Evt_IContact_Held:
			fireDeviceStatusChangedEvent(event);
			break;

		case Evt_IContact_BeHeld:
			fireDeviceStatusChangedEvent(event);
			break;

		case Evt_IContact_ConsultOffHook:
			fireDeviceStatusChangedEvent(event);
			break;

		case Evt_IContact_ConsultDialing:

			fireDeviceStatusChangedEvent(event);

			if (actionType === ACTION_TYPE_TRANSFER && config.transferFlag === TRANSFER_FLAG_DIALING) {
				forwardCall();
			}
			break;

		case Evt_IContact_ConsultConnected:

			fireDeviceStatusChangedEvent(event);

			if (actionType === ACTION_TYPE_CONFERENCE) {
				doMeeting();
			}

			if (actionType === ACTION_TYPE_TRANSFER) {
				forwardCall();
			}
			break;

		case Evt_IContact_BeConsultOffering:
			fireDeviceStatusChangedEvent(event);
			fireOfferingEvent(event);
			autoAnswerCall();
			break;

		case Evt_IContact_BeConsultConnected:
			fireDeviceStatusChangedEvent(event);
			fireConnectedEvent(event);
			break;

		case Evt_IContact_ConsultFailed:
			fireDeviceStatusChangedEvent(event);
			actionType = ACTION_TYPE_NULL;
			break;

		case Evt_IContact_Conferenced:
			fireDeviceStatusChangedEvent(event);
			break;

		case Evt_IContact_BeConferenced:
			fireDeviceStatusChangedEvent(event);
			fireConnectedEvent(event);
			break;

		case Evt_IContact_ForceInserted:
			fireDeviceStatusChangedEvent(event);
			answerCall();
			break;

		case Evt_IContact_Listened:
			fireDeviceStatusChangedEvent(event);
			answerCall();
			break;

		case Evt_IContact_Recording:
			fireDeviceStatusChangedEvent(event);
			break;

		case Evt_IContact_RecordEnd:
			fireDeviceStatusChangedEvent(event);
			fireRecordEndEvent(event);
			break;

		case Evt_IContact_DeviceMuting:
			fireDeviceStatusChangedEvent(event);
			break;

		case Evt_IContact_DeviceMuteEnd:
			fireDeviceStatusChangedEvent(event);
			break;

		case Evt_IContact_DevicePlaying:
			fireDeviceStatusChangedEvent(event);
			break;

		case Evt_IContact_DevicePlayEnd:
			fireDeviceStatusChangedEvent(event);
			break;
		}
	}

	function onErrorEvent(event) {

		switch (event.MessageID) {
		case Evt_IContact_ASFailure:
		case Evt_IContact_UniversalFailure:

			// 坐席工号占用时执行强制签入
			if (config.forceLogin && event.Key_IContact_UniversalFailureDesc == 'Err_IContact_AgentIDInService') {

				if (loginTimer) {
					clearInterval(loginTimer);
				}

				forceLogin();
			} else {
				fireErrorEvent(event);
			}

			break;
		}
	}

	function onSystemEvent(event) {

		var reasonCode = event.Key_IContact_Reason;
		var timestamp = event.Key_IContact_TimeStamp;

		switch (event.MessageID) {

		case Evt_ISystem_AgentClientOnLine:
			isOnline = true;
			fireOnlineEvent(REASON_AGENTCLIENT, timestamp);
			fireSystemStatusChangedEvent(event.MessageID, SST_ONLINE, REASON_AGENTCLIENT, timestamp);
			break;

		case Evt_ISystem_AgentClientOffLine:
			isOnline = false;
			fireSystemStatusChangedEvent(event.MessageID, SST_OFFLINE, REASON_AGENTCLIENT, timestamp);
			fireOfflineEvent(REASON_AGENTCLIENT, timestamp);
			break;

		case Evt_ISystem_AgentProxyOnLine:
			isOnline = true;
			fireOnlineEvent(REASON_AGENTPROXY, timestamp);
			fireSystemStatusChangedEvent(event.MessageID, SST_ONLINE, REASON_AGENTPROXY, timestamp);
			break;

		case Evt_ISystem_AgentProxyOffLine:
			isOnline = false;
			fireSystemStatusChangedEvent(event.MessageID, SST_OFFLINE, REASON_AGENTPROXY, timestamp);
			fireOfflineEvent(REASON_AGENTPROXY, timestamp);
			break;

		case Evt_IContact_AgentServiceOnLine:
			isOnline = true;
			fireOnlineEvent(REASON_AGENTSERVICE, timestamp);
			fireSystemStatusChangedEvent(event.MessageID, SST_ONLINE, REASON_AGENTSERVICE, timestamp);
			break;

		case Evt_IContact_AgentServiceOffLine:
			isOnline = false;
			fireSystemStatusChangedEvent(event.MessageID, SST_OFFLINE, REASON_AGENTSERVICE, timestamp);
			fireOfflineEvent(REASON_AGENTSERVICE, timestamp);
			break;

		case Evt_IContact_LinkUp:
			fireSystemStatusChangedEvent(event.MessageID, SST_LINKUP, reasonCode, timestamp);
			break;

		case Evt_IContact_LinkDown:
			fireSystemStatusChangedEvent(event.MessageID, SST_LINKDOWN, reasonCode, timestamp);
			break;
		}
	}

	function onAgentLogin(event) {

		if (loginTimer) {
			clearInterval(loginTimer);
		}

		if (event.Key_IContact_PrivateData) {
			agent.agentIp = event.Key_IContact_PrivateData.Key_IContact_AgentIP;
			agent.loginTime = event.Key_IContact_PrivateData.Key_IContact_AgentLoginTime;
		}

		agent.isLogin = true;
		agent.loginId = event.Key_IContact_AgentLoginID;
		agent.sessionId = event.Key_IContact_SessionID;

		setCookie('sid-' + agent.agentId, agent.sessionId);
	}

	function onAgentNotReady(event) {
		if (config.loginReady && event.Key_IContact_LastStatus == AST_LOGIN) {
			ready();
		}
	}

	function onAgentLogout(event) {
		agent.isLogin = false;
		agent.sessionId = null;
		expireCookie('sid-' + agent.agentId);
	}

	function onAgentAfterCallWork(event) {

		if (appointType == APPOINT_TYPE_NOTREADY) {
			appointType = APPOINT_TYPE_NULL;
			notReady();
			return;
		}

		if (appointType == APPOINT_TYPE_AFTERCALLWORK) {
			appointType = APPOINT_TYPE_NULL;
			return;
		}

		if (appointType == APPOINT_TYPE_OTHERWORK) {
			appointType = APPOINT_TYPE_NULL;
			otherWork(0);
			return;
		}

		if (acwTimer) {
			clearTimeout(acwTimer);
		}

		acwTimer = setTimeout(function() {
			switch (config.autoReady) {
			case AUTO_TYPE_AGENTREADY:
				ready();
				break;

			case AUTO_TYPE_AGENTNOTREADY:
				notReady();
				break;
			}
		}, config.acwTime);
	}

	function clearAcwTimer(status) {

		if (acwTimer && status != AST_AFTERCALLWORK) {
			clearTimeout(acwTimer);
		}
	}

	function fireAgentStatusChangedEvent(event) {

		var currStatus = event.Key_IContact_AgentStatus;
		var lastStatus = event.Key_IContact_LastStatus || 0;

		if (event.MessageID == Evt_IContact_AgentLogout) {
			if (currStatus == undefined) {
				currStatus = AST_LOGOUT;
			}
		}

		if (currStatus != lastStatus) {
			agent.status = currStatus;
			clearAcwTimer(currStatus);
		}

		if (isFunction(self.onStatusChanged)) {
			self.onStatusChanged({
				eventType : EVENT_TYPE_AGENT,
				eventName : event.MessageID.replace(/Evt_IContact_/, ''),
				currStatus : currStatus,
				lastStatus : lastStatus,
				reasonCode : event.Key_IContact_Reason,
				startTime : event.Key_IContact_StartTime,
				loginId : event.Key_IContact_AgentLoginID || agent.loginId,
				agentId : event.Key_IContact_AgentID ||  agent.agentId,
				agentDn : event.Key_IContact_AgentDN,
				contactId : event.Key_IContact_ContactID
			});
		}
	}

	function fireDeviceStatusChangedEvent(event) {

		if (event.Key_IContact_CurrStatus != event.Key_IContact_LastStatus) {
			agent.status = event.Key_IContact_CurrStatus;
			clearAcwTimer(event.Key_IContact_CurrStatus);
		}

		if (isFunction(self.onStatusChanged)) {
			self.onStatusChanged({
				eventType : EVENT_TYPE_DEVICE,
				eventName : event.MessageID.replace(/Evt_IContact_/, ''),
				currStatus : event.Key_IContact_CurrStatus,
				lastStatus : event.Key_IContact_LastStatus,
				reasonCode : event.Key_IContact_Reason,
				reasonDesc : event.Key_IContact_ReasonDesc || '',
				startTime : event.Key_IContact_StartTime,
				contactId : event.Key_IContact_ContactID,
				oriAni : event.Key_IContact_ANI,
				oriDnis : event.Key_IContact_DNIS,
				deviceDn : event.Key_IContact_DeviceDN,
				deviceType : event.Key_IContact_DeviceType,
				channelType : event.Key_IContact_ChannelType,
				callType : event.Key_IContact_CallType,
				callDirection : event.Key_IContact_CallDirection,
				callId : event.Key_IContact_CallID,
				callingParty : event.Key_IContact_CallingParty,
				calledParty : event.Key_IContact_CalledParty,
				thirdParty : event.Key_IContact_ThirdParty,
				otherParty : event.Key_IContact_OtherParty,
				timestamp : event.Key_IContact_TimeStamp,
				fileName : event.Key_IContact_FileName,
				fileDuration : event.Key_IContact_TimeLen,
				ucid : event.Key_IContact_UniversalCallID,
				scid : event.Key_IContact_SipCallID,
				headKey : event.Key_IContact_HeadKey,
				headValue : event.Key_IContact_HeadValue
			});
		}
	}

	function fireSystemStatusChangedEvent(eventName, currStatus, reasonCode, startTime) {

		clearAcwTimer(currStatus);

		if (isFunction(self.onStatusChanged)) {
			self.onStatusChanged({
				eventType : EVENT_TYPE_SYSTEM,
				eventName : eventName.replace(/Evt_IContact_/, '').replace(/Evt_ISystem_/, ''),
				currStatus : currStatus,
				reasonCode : reasonCode,
				startTime : startTime
			});
		}
	}

	function fireOnlineEvent(reasonCode, startTime) {
		if (isFunction(self.onOnline)) {
			self.onOnline({
				reasonCode: reasonCode,
				startTime: startTime
			});
		}

		// 自动签入
		if (config.autoLogin && agent.status == -1) {
			login();
		}

		// 重连自动签入
		if (agent.isLogin) {

			if (loginTimer) {
				clearInterval(loginTimer);
			}

			login();

			// 每6秒重试一次
			loginTimer = setInterval(function() {
				login();
			}, 6000);
		}
	}

	function fireOfflineEvent(reasonCode, startTime) {
		if (isFunction(self.onOffline)) {
			self.onOffline({
				reasonCode: reasonCode,
				startTime: startTime
			});
		}
	}

	function fireLoginEvent(event) {
		if (isFunction(self.onLogin)) {
			self.onLogin({
				agentId: agent.agentId,
				agentDn: agent.agentDn,
				agentName: agent.agentName,
				agentType: agent.agentType,
				agentIp: agent.agentIp,
				phoneIp: agent.phoneIp,
				skillDesc: agent.skillDesc,
				tenantDesc: agent.tenantDesc,
				acdAgentId: agent.acdAgentId,
				acdAgentGroup: agent.acdAgentGroup,
				acdAgentPassword: agent.acdAgentPassword,
				camponFlag: agent.camponFlag,
				loginId: agent.loginId,
				loginTime: event.loginTime,
				reasonCode: event.Key_IContact_Reason,
				sessionId: event.Key_IContact_SessionID
			});
		}
	}

	function fireLogoutEvent(event) {
		if (isFunction(self.onLogout)) {
			self.onLogout({
				agentId: agent.agentId,
				agentDn: agent.agentDn,
				loginId: agent.loginId,
				logoutTime: event.Key_IContact_StartTime,
				sessionId: event.Key_IContact_SessionID
			});
		}
	}

	function fireDialingEvent(event) {
		if (isFunction(self.onDialing)) {
			self.onDialing({
				contactId : event.Key_IContact_ContactID,
				currStatus : event.Key_IContact_CurrStatus,
				lastStatus : event.Key_IContact_LastStatus,
				oriAni : event.Key_IContact_ANI,
				oriDnis : event.Key_IContact_DNIS,
				deviceDn : event.Key_IContact_DeviceDN,
				deviceType : event.Key_IContact_DeviceType,
				channelType : event.Key_IContact_ChannelType,
				callType : event.Key_IContact_CallType,
				callDirection : event.Key_IContact_CallDirection,
				callId : event.Key_IContact_CallID,
				callingParty : event.Key_IContact_CallingParty,
				calledParty : event.Key_IContact_CalledParty,
				otherParty : event.Key_IContact_OtherParty,
				tenantId: event.Key_IContact_TenantID,
				reasonCode : event.Key_IContact_Reason,
				reasonDesc : event.Key_IContact_ReasonDesc || '',
				startTime : event.Key_IContact_StartTime,
				ucid : event.Key_IContact_UniversalCallID,
				scid : event.Key_IContact_SipCallID || '',
				headKey : event.Key_IContact_HeadKey,
				headValue : event.Key_IContact_HeadValue
			});
		}
	}

	function fireOfferingEvent(event) {
		if (isFunction(self.onOffering)) {
			self.onOffering({
				contactId : event.Key_IContact_ContactID,
				currStatus : event.Key_IContact_CurrStatus,
				lastStatus : event.Key_IContact_LastStatus,
				oriAni : event.Key_IContact_ANI,
				oriDnis : event.Key_IContact_DNIS,
				deviceDn : event.Key_IContact_DeviceDN,
				deviceType : event.Key_IContact_DeviceType,
				channelType : event.Key_IContact_ChannelType,
				callType : event.Key_IContact_CallType,
				callDirection : event.Key_IContact_CallDirection,
				callId : event.Key_IContact_CallID,
				callingParty : event.Key_IContact_CallingParty,
				calledParty : event.Key_IContact_CalledParty,
				otherParty : event.Key_IContact_OtherParty,
				tenantId: event.Key_IContact_TenantID,
				reasonCode : event.Key_IContact_Reason,
				reasonDesc : event.Key_IContact_ReasonDesc || '',
				startTime : event.Key_IContact_StartTime,
				ucid : event.Key_IContact_UniversalCallID,
				scid : event.Key_IContact_SipCallID || '',
				headKey : event.Key_IContact_HeadKey,
				headValue : event.Key_IContact_HeadValue
			});
		}
	}

	function fireConnectedEvent(event) {
		if (isFunction(self.onConnected)) {
			self.onConnected({
				contactId : event.Key_IContact_ContactID,
				currStatus : event.Key_IContact_CurrStatus,
				lastStatus : event.Key_IContact_LastStatus,
				oriAni : event.Key_IContact_ANI,
				oriDnis : event.Key_IContact_DNIS,
				deviceDn : event.Key_IContact_DeviceDN,
				deviceType : event.Key_IContact_DeviceType,
				channelType : event.Key_IContact_ChannelType,
				callType : event.Key_IContact_CallType,
				callDirection : event.Key_IContact_CallDirection,
				callId : event.Key_IContact_CallID,
				callingParty : event.Key_IContact_CallingParty,
				calledParty : event.Key_IContact_CalledParty,
				otherParty : event.Key_IContact_OtherParty,
				tenantId: event.Key_IContact_TenantID,
				reasonCode : event.Key_IContact_Reason,
				reasonDesc : event.Key_IContact_ReasonDesc || '',
				startTime : event.Key_IContact_StartTime,
				ucid : event.Key_IContact_UniversalCallID,
				scid : event.Key_IContact_SipCallID || '',
				headKey : event.Key_IContact_HeadKey,
				headValue : event.Key_IContact_HeadValue
			});
		}
	}

	function fireReleasedEvent(event) {
		if (isFunction(self.onReleased)) {
			self.onReleased({
				contactId : event.Key_IContact_ContactID,
				currStatus : event.Key_IContact_CurrStatus,
				lastStatus : event.Key_IContact_LastStatus,
				oriAni : event.Key_IContact_ANI,
				oriDnis : event.Key_IContact_DNIS,
				deviceDn : event.Key_IContact_DeviceDN,
				deviceType : event.Key_IContact_DeviceType,
				channelType : event.Key_IContact_ChannelType,
				callType : event.Key_IContact_CallType,
				callDirection : event.Key_IContact_CallDirection,
				callId : event.Key_IContact_CallID,
				callingParty : event.Key_IContact_CallingParty,
				calledParty : event.Key_IContact_CalledParty,
				otherParty : event.Key_IContact_OtherParty,
				tenantId: event.Key_IContact_TenantID,
				reasonCode : event.Key_IContact_Reason,
				reasonDesc : event.Key_IContact_ReasonDesc || '',
				startTime : event.Key_IContact_StartTime,
				ucid : event.Key_IContact_UniversalCallID,
				scid : event.Key_IContact_SipCallID || '',
				headKey : event.Key_IContact_HeadKey,
				headValue : event.Key_IContact_HeadValue
			});
		}
	}

	function fireIMOfferingEvent(event) {
		if (isFunction(self.onIMOffering)) {
			self.onIMOffering({
				contactId : event.Key_IContact_ContactID,
				oriAni : event.Key_IContact_ANI,
				oriDnis : event.Key_IContact_DNIS,
				deviceDn : event.Key_IContact_DeviceDN,
				deviceType : event.Key_IContact_DeviceType,
				channelType : event.Key_IContact_ChannelType,
				callType : event.Key_IContact_CallType,
				callDirection : event.Key_IContact_CallDirection,
				tenantId: event.Key_IContact_TenantID,
				reasonCode : event.Key_IContact_Reason,
				reasonDesc : event.Key_IContact_ReasonDesc || '',
				startTime : event.Key_IContact_StartTime,
				ucid : event.Key_IContact_UniversalCallID,
				scid : event.Key_IContact_SipCallID || '',
				imCallId: event.Key_IContact_IMCallID || '',
				headKey : event.Key_IContact_HeadKey,
				headValue : event.Key_IContact_HeadValue
			});
		}
	}

	function fireIMConnectedEvent(event) {
		if (isFunction(self.onIMConnected)) {
			self.onIMConnected({
				contactId : event.Key_IContact_ContactID,
				oriAni : event.Key_IContact_ANI,
				oriDnis : event.Key_IContact_DNIS,
				deviceDn : event.Key_IContact_DeviceDN,
				deviceType : event.Key_IContact_DeviceType,
				channelType : event.Key_IContact_ChannelType,
				callType : event.Key_IContact_CallType,
				callDirection : event.Key_IContact_CallDirection,
				tenantId: event.Key_IContact_TenantID,
				reasonCode : event.Key_IContact_Reason,
				reasonDesc : event.Key_IContact_ReasonDesc || '',
				startTime : event.Key_IContact_StartTime,
				ucid : event.Key_IContact_UniversalCallID,
				scid : event.Key_IContact_SipCallID || '',
				imCallId: event.Key_IContact_IMCallID || '',
				headKey : event.Key_IContact_HeadKey,
				headValue : event.Key_IContact_HeadValue
			});
		}
	}

	function fireIMReleasedEvent(event) {
		if (isFunction(self.onIMReleased)) {
			self.onIMReleased({
				contactId : event.Key_IContact_ContactID,
				oriAni : event.Key_IContact_ANI,
				oriDnis : event.Key_IContact_DNIS,
				deviceDn : event.Key_IContact_DeviceDN,
				deviceType : event.Key_IContact_DeviceType,
				channelType : event.Key_IContact_ChannelType,
				callType : event.Key_IContact_CallType,
				callDirection : event.Key_IContact_CallDirection,
				tenantId: event.Key_IContact_TenantID,
				reasonCode : event.Key_IContact_Reason,
				reasonDesc : event.Key_IContact_ReasonDesc || '',
				startTime : event.Key_IContact_StartTime,
				ucid : event.Key_IContact_UniversalCallID,
				scid : event.Key_IContact_SipCallID || '',
				imCallId: event.Key_IContact_IMCallID || '',
				headKey : event.Key_IContact_HeadKey,
				headValue : event.Key_IContact_HeadValue
			});
		}
	}

	function fireRecordEndEvent(event) {
		if (isFunction(self.onRecordEnd)) {
			self.onRecordEnd({
				contactId : event.Key_IContact_ContactID,
				oriAni : event.Key_IContact_ANI,
				oriDnis : event.Key_IContact_DNIS,
				deviceDn : event.Key_IContact_DeviceDN,
				deviceType : event.Key_IContact_DeviceType,
				channelType : event.Key_IContact_ChannelType,
				callType : event.Key_IContact_CallType,
				callDirection : event.Key_IContact_CallDirection,
				callId : event.Key_IContact_CallID,
				callingParty : event.Key_IContact_CallingParty,
				calledParty : event.Key_IContact_CalledParty,
				otherParty : event.Key_IContact_OtherParty,
				recordTime : event.Key_IContact_TimeStamp,
				recordFile : event.Key_IContact_FileName,
				recordDuration : event.Key_IContact_TimeLen,
				tenantId: event.Key_IContact_TenantID,
				reasonCode : event.Key_IContact_Reason,
				reasonDesc : event.Key_IContact_ReasonDesc || '',
				startTime : event.Key_IContact_StartTime,
				ucid : event.Key_IContact_UniversalCallID,
				scid : event.Key_IContact_SipCallID || '',
				headKey : event.Key_IContact_HeadKey,
				headValue : event.Key_IContact_HeadValue
			});
		}
	}

	function fireErrorEvent(event) {
		if (isFunction(self.onError)) {
			self.onError({
				errorCode : event.Key_IContact_UniversalFailureID,
				errorDesc : event.Key_IContact_UniversalFailureDesc,
				startTime : event.Key_IContact_TimeStamp
			});
		}
	}

	//清除循环登录任务
	function clearLoginTimer(){
		if(loginTimer){
			clearInterval(loginTimer);
		}
	}

	//获取socket信息
	function getSocketInfo(){
		return socket;
	}

	function isObject(obj) {
		var type = typeof obj;
		return !!obj && (type == 'object' || type == 'function');
	}

	function isObjectLike(obj) {
		return !!obj && typeof obj == 'object';
	}

	function isArray(obj) {
		return isObjectLike(obj) && Object.prototype.toString.call(obj) == '[object Array]';
	}

	function isFunction(obj) {
		return isObject(obj) && Object.prototype.toString.call(obj) == '[object Function]';
	}

	function isNumber(obj) {
		return typeof obj == 'number' || (isObjectLike(obj) && Object.prototype.toString.call(obj) == '[object Number]');
	}

	function isString(obj) {
		return typeof obj == 'string' || (isObjectLike(obj) && Object.prototype.toString.call(obj) == '[object String]');
	}

	function log(message, data) {
		if (window.console && console.log) {
			if (isObject(data)) {
				if (JSON && JSON.stringify) {
					console.log(message, JSON.stringify(data, null, 2));
				} else {
					console.log(message, data);
				}
			} else {
				console.log(message, data);
			}
		}
	}

	function debug(message, data) {
		if (config.debug) {
			log(message, data);
		}
	}

	function httpGet(url, success, error) {

		try {
			if (url) {
				debug('http get:', url);

				var xhr = new XMLHttpRequest();

				if ('withCredentials' in xhr) {

					xhr.onreadystatechange = function() {
						if (xhr.readyState === 4) {
							if (xhr.status === 200) {
								if (isFunction(success)) {
									success(xhr.responseText);
								}
							} else {
								if (isFunction(error)) {
									error(xhr.status, xhr.statusText);
								}
							}
						}
					};

					xhr.open('GET', url);
					xhr.send();

				} else if (window.XDomainRequest) {

					var xdr = new XDomainRequest();

					xdr.onload = function(e) {
						if (isFunction(success)) {
							success(xdr.responseText);
						}
					};

					xdr.onerror = function(e) {
						if (isFunction(error)) {
							error();
						}
					};

					xdr.open('GET', url);
					setTimeout(function() {
						xdr.send();
					}, 0);
				}
			}
		} catch (e) {
			log('http error:', e ? e.message : e);
		}
	}

	function buildHttpUrl(actionUrl) {

		var httpUrl = actionUrl;
		var phoneIp = agent.phoneIp;

		if (httpUrl && isString(httpUrl)) {
			if (httpUrl.substring(0, 7).toLowerCase() == 'http://' || httpUrl.substring(0, 8).toLowerCase() == 'https://') {
				return httpUrl;
			} else {
				if (phoneIp && isString(phoneIp)) {
					if (phoneIp.substring(0, 7).toLowerCase() == 'http://') {
						return phoneIp + ('/' + httpUrl).replace('//', '/');
					} else {
						return 'http://' + phoneIp + ('/' + httpUrl).replace('//', '/');
					}
				} else {
					return null;
				}
			}
		} else {
			return null;
		}
	}

	function getCookie(key) {
		return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*"
			+ encodeURIComponent(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
	}

	function setCookie(key, value, end, path, domain, secure) {

		if (!key || /^(?:expires|max\-age|path|domain|secure)$/i.test(key)) {
			return false;
		}

		var expires = "";

		if (end) {
			switch (end.constructor) {
			case Number:
				expires = end === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + end;
				break;
			case String:
				expires = "; expires=" + end;
				break;
			case Date:
				expires = "; expires=" + end.toUTCString();
				break;
			}
		}

		document.cookie = encodeURIComponent(key) + "=" + encodeURIComponent(value) + expires
			+ (domain ? "; domain=" + domain : "") + (path ? "; path=" + path : "")	+ (secure ? "; secure" : "");

		return true;
	}

	function hasCookie(key) {
		return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
	}

	function expireCookie(key, path, domain) {
		if (!key || !hasCookie(key)) {
			return false;
		}
		document.cookie = encodeURIComponent(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT"
				+ (domain ? "; domain=" + domain : "") + (path ? "; path=" + path : "");
		return true;
	}

	var EVENT_TYPE_AGENT = 1;
	var EVENT_TYPE_DEVICE = 2;
	var EVENT_TYPE_SYSTEM = 3;

	var AGENT_TYPE_NORMAL = 1; 	// 普通坐席
	var AGENT_TYPE_ACD = 2; 	// ACD坐席
	var AGENT_TYPE_VIRTUAL = 3; // 虚拟坐席
	var AGENT_TYPE_SOFTACD = 4; // SoftACD坐席
	var AGENT_TYPE_PHONE = 5; 	// 手机坐席

	var AST_LOGIN = 0;
	var AST_NOTREADY = 3;
	var AST_READY = 1;
	var AST_LOCKED = 5;
	var AST_WORKING = 2;
	var AST_AFTERCALLWORK = 6;
	var AST_OTHERWORK = 6;
	var AST_LOGOUT = 7;
	var DST_NULL = 20;
	var DST_IDLE = 21;
	var DST_OFFHOOK = 22;
	var DST_OFFERING = 23;
	var DST_BECONSULTOFFERING = 24;
	var DST_DIALING = 25;
	var DST_CONSULTOFFHOOK = 26;
	var DST_CONSULTDIALING = 27;
	var DST_CONNECTED = 28;
	var DST_HELD = 29;
	var DST_BEHELD = 30;
	var DST_CONSULTCONNECTED = 31;
	var DST_BECONSULTCONNECTED = 32;
	var DST_CONFERENCED = 33;
	var DST_BECONFERENCED = 34;
	var DST_LISTENED = 35;
	var DST_FORCEINSERTED = 36;
	var SST_ONLINE = 50;
	var SST_OFFLINE = 51;
	var SST_LINKUP = 52;
	var SST_LINKDOWN = 53;

	var REASON_AGENTCLIENT = 1; // 事件原因：AgentClient
	var REASON_AGENTPROXY = 2; // 事件原因：AgentProxy
	var REASON_AGENTSERVICE = 3; // 事件原因：AgentService

	var ACTION_TYPE_NULL = 0; // 没有动作
	var ACTION_TYPE_TRANSFER = 1; // 转接动作
	var ACTION_TYPE_CONFERENCE = 2; // 会议动作

	var APPOINT_TYPE_NULL = 0; // 取消预约
	var APPOINT_TYPE_NOTREADY = 1; // 预约忙碌
	var APPOINT_TYPE_AFTERCALLWORK = 2; // 预约整理
	var APPOINT_TYPE_OTHERWORK = 3; // 预约其他

	var DEST_TYPE_INTERDN = 1; // 分机
	var DEST_TYPE_AGENTID = 2; // 工号
	var DEST_TYPE_EXTERDN = 3; // 外线
	var DEST_TYPE_SKILLGROUP = 4; // 技能
	var DEST_TYPE_SATISFACTION = 5; // 转满意度
	var DEST_TYPE_PASSWORD = 6; // 转密码验证
	var DEST_TYPE_CONSULT2TRANSFER = 7; // 咨询变转接
	var DEST_TYPE_CONSULT2CONFERENCE = 8; // 咨询变会议
	var DEST_TYPE_TRANSFERIVR = 9; // 转IVR语音

	var TRANSFER_TYPE_CONSULT = 1; // 咨询转接
	var TRANSFER_TYPE_SINGLESTEP = 2; // 单步转接

	var TRANSFER_FLAG_CONNECTED = 1; // 通话转
	var TRANSFER_FLAG_DIALING = 2; // 振铃转

	var AUTO_TYPE_AGENTACW = 1; // 事后整理
	var AUTO_TYPE_AGENTREADY = 2; // 自动空闲
	var AUTO_TYPE_AGENTNOTREADY = 3; // 自动忙碌

	// 状态控制命令
	var Cmd_IContact_AgentLogin = 'Cmd_IContact_AgentLogin';//登录
	var Cmd_IContact_AgentLogout = 'Cmd_IContact_AgentLogout';//登出
	var Cmd_IContact_AgentNotReady = 'Cmd_IContact_AgentNotReady';//置忙
	var Cmd_IContact_AgentReady = 'Cmd_IContact_AgentReady';//置闲
	var Cmd_IContact_AgentOtherWork = 'Cmd_IContact_AgentOtherWork';//座席变更至其它工作

	// 呼叫控制命令
	var Cmd_IContact_MakeCall = 'Cmd_IContact_MakeCall';//外呼
	var Cmd_IContact_AnswerCall = 'Cmd_IContact_AnswerCall';//应答
	var Cmd_IContact_HangupCall = 'Cmd_IContact_HangupCall';//挂断
	var Cmd_IContact_ClearCall = 'Cmd_IContact_ClearCall';
	var Cmd_IContact_HoldCall = 'Cmd_IContact_HoldCall';//保持
	var Cmd_IContact_RetrieveCall = 'Cmd_IContact_RetrieveCall';//接回
	var Cmd_IContact_TransferCall = 'Cmd_IContact_TransferCall';//转接
	var Cmd_IContact_ConferenceCall = 'Cmd_IContact_ConferenceCall';
	var Cmd_IContact_ConsultCall = 'Cmd_IContact_ConsultCall';//咨询
	var Cmd_IContact_ConsultReconnect = 'Cmd_IContact_ConsultReconnect';//咨询接回
	var Cmd_IContact_ReconnectCall = 'Cmd_IContact_ReconnectCall';//重连
	var Cmd_IContact_DeflectCall = 'Cmd_IContact_DeflectCall';
	var Cmd_IContact_PickupCall = 'Cmd_IContact_PickupCall';
	var Cmd_IContact_SendDTMF = 'Cmd_IContact_SendDTMF';
	var Cmd_IContact_SingleStepTransfer = 'Cmd_IContact_SingleStepTransfer';
	var Cmd_IContact_TransferIVR = 'Cmd_IContact_TransferIVR';//转接IVR
	var Cmd_IContact_AssistService = 'Cmd_IContact_AssistService';//坐席协作

	// 质检命令
	var Cmd_IContact_ForceAgentLogout = 'Cmd_IContact_ForceAgentLogout';
	var Cmd_IContact_ForceAgentReady = 'Cmd_IContact_ForceAgentReady';
	var Cmd_IContact_ForceAgentNotReady = 'Cmd_IContact_ForceAgentNotReady';
	var Cmd_IContact_Listen = 'Cmd_IContact_Listen';//监听
	var Cmd_IContact_ForceInsert = 'Cmd_IContact_ForceInsert';//强插
	var Cmd_IContact_Intercept = 'Cmd_IContact_Intercept';//拦截
	var Cmd_IContact_ForceHangup = 'Cmd_IContact_ForceHangup';//强拆
	var Cmd_IContact_Whisper = 'Cmd_IContact_Whisper';//密语


	// 随路数据命令
	var Cmd_IContact_SetAssociateData = 'Cmd_IContact_SetAssociateData';
	var Cmd_IContact_GetAssociateData = 'Cmd_IContact_GetAssociateData';

	// 其他命令
	var Cmd_IContact_GetAgentState = 'Cmd_IContact_GetAgentState';
	var Cmd_IContact_StartRecord = 'Cmd_IContact_StartRecord';
	var Cmd_IContact_StopRecord = 'Cmd_IContact_StopRecord';
	var Cmd_IContact_AgentRouteSelect = 'Cmd_IContact_AgentRouteSelect';
	var Cmd_IContact_StartMute = 'Cmd_IContact_StartMute';
	var Cmd_IContact_StopMute = 'Cmd_IContact_StopMute';
	var Cmd_IContact_SetSipHead = 'Cmd_IContact_SetSipHead';
	var Cmd_IContact_GetSipHead = 'Cmd_IContact_GetSipHead';
	var Cmd_IContact_ExecCommand = 'Cmd_IContact_ExecCommand';
	var Cmd_IContact_GetAgentList = 'Cmd_IContact_GetAgentList';//座席列表
	var Cmd_IContact_GetDialCaller = 'Cmd_IContact_GetDialCaller';//获取外显号码
	var Cmd_IContact_Reset = 'Cmd_IContact_Reset';//重置




	// 响应消息
	var Resp_IContact_AgentLogin = 'Resp_IContact_AgentLogin';
	var Resp_IContact_AgentLogout = 'Resp_IContact_AgentLogout';
	var Resp_IContact_GetCallData = 'Resp_IContact_GetCallData';
	var Resp_IContact_GetAgentState = 'Resp_IContact_GetAgentState';
	var Resp_IContact_GetAgentData = 'Resp_IContact_GetAgentData';//坐席人员
	var Resp_IContact_GetDialCaller = 'Resp_IContact_GetDialCaller';//坐席外线号码
	var Resp_IContact_Listen = 'Resp_IContact_Listen';//监听
	var Resp_IContact_GetAssociateData = 'Resp_IContact_GetAssociateData';//获取随路数据
	var Resp_IContact_Whisper = 'Resp_IContact_Whisper';//密语


	// 坐席事件
	var Evt_IContact_AgentLogin = 'Evt_IContact_AgentLogin';
	var Evt_IContact_AgentLogout = 'Evt_IContact_AgentLogout';
	var Evt_IContact_AgentReady = 'Evt_IContact_AgentReady';
	var Evt_IContact_AgentNotReady = 'Evt_IContact_AgentNotReady';
	var Evt_IContact_AgentOtherWork = 'Evt_IContact_AgentOtherWork';
	var Evt_IContact_AgentAfterCallWork = 'Evt_IContact_AgentAfterCallWork';
	var Evt_IContact_AgentLocked = 'Evt_IContact_AgentLocked';
	var Evt_IContact_AgentWorking = 'Evt_IContact_AgentWorking';
	var Evt_IContact_ASFailure = 'Evt_IContact_ASFailure';
	var Evt_IContact_UniversalFailure = 'Evt_IContact_UniversalFailure';
	var Evt_AgentStatus_Consulted = 'Evt_AgentStatus_Consulted';

	// 设备事件
	var Evt_IContact_OffHook = 'Evt_IContact_OffHook';
	var Evt_IContact_Dialing = 'Evt_IContact_Dialing';
	var Evt_IContact_Offering = 'Evt_IContact_Offering';
	var Evt_IContact_Connected = 'Evt_IContact_Connected';
	var Evt_IContact_Released = 'Evt_IContact_Released';
	var Evt_IContact_Failed = 'Evt_IContact_Failed';
	var Evt_IContact_Notify = 'Evt_IContact_Notify';
	var Evt_IContact_Held = 'Evt_IContact_Held';
	var Evt_IContact_BeHeld = 'Evt_IContact_BeHeld';
	var Evt_IContact_ConsultOffHook = 'Evt_IContact_ConsultOffHook';
	var Evt_IContact_ConsultDialing = 'Evt_IContact_ConsultDialing';
	var Evt_IContact_ConsultConnected = 'Evt_IContact_ConsultConnected';
	var Evt_IContact_BeConsultOffering = 'Evt_IContact_BeConsultOffering';
	var Evt_IContact_BeConsultConnected = 'Evt_IContact_BeConsultConnected';
	var Evt_IContact_ConsultFailed = 'Evt_IContact_ConsultFailed';
	var Evt_IContact_Conferenced = 'Evt_IContact_Conferenced';
	var Evt_IContact_BeConferenced = 'Evt_IContact_BeConferenced';
	var Evt_IContact_ForceInserted = 'Evt_IContact_ForceInserted';
	var Evt_IContact_Listened = 'Evt_IContact_Listened';
	var Evt_IContact_Recording = 'Evt_IContact_Recording';
	var Evt_IContact_RecordEnd = 'Evt_IContact_RecordEnd';

	var Evt_IContact_IMOffering = 'Evt_IContact_IMOffering';
	var Evt_IContact_IMConnected = 'Evt_IContact_IMConnected';
	var Evt_IContact_IMReleased = 'Evt_IContact_IMReleased';

	var Evt_IContact_DeviceMuting = 'Evt_IContact_DeviceMuting';
	var Evt_IContact_DeviceMuteEnd = 'Evt_IContact_DeviceMuteEnd';
	var Evt_IContact_SetSipHeadEnd = 'Evt_IContact_SetSipHeadEnd';
	var Evt_IContact_GetSipHeadEnd = 'Evt_IContact_GetSipHeadEnd';
	var Evt_IContact_DevicePlaying = 'Evt_IContact_DevicePlaying';
	var Evt_IContact_DevicePlayEnd = 'Evt_IContact_DevicePlayEnd';

	// 系统事件
	var Evt_IContact_AgentServiceOnLine = 'Evt_IContact_AgentServiceOnLine';
	var Evt_IContact_AgentServiceOffLine = 'Evt_IContact_AgentServiceOffLine';
	var Evt_IContact_LinkUp = 'Evt_IContact_LinkUp';
	var Evt_IContact_LinkDown = 'Evt_IContact_LinkDown';
	var Evt_ISystem_AgentProxyOnLine = 'Evt_ISystem_AgentProxyOnLine';
	var Evt_ISystem_AgentProxyOffLine = 'Evt_ISystem_AgentProxyOffLine';
	var Evt_ISystem_AgentClientOnLine = 'Evt_ISystem_AgentClientOnLine';
	var Evt_ISystem_AgentClientOffLine = 'Evt_ISystem_AgentClientOffLine';

	this.version = version;

	this.getSocketInfo = function() {
		return getSocketInfo();
	};

	this.getOption = function(field) {
		return getOption(field);
	};

	this.setOption = function(option) {
		return setOption(option);
	};

	this.start = function() {
		return start();
	};

	this.stop = function() {
		return stop();
	};

	// 签入
	this.login = function(isForce) {
		return login(isForce);
	};

	// 签出
	this.logout = function() {
		return logout();
	};

	// 忙碌
	this.notReady = function() {
		return notReady();
	};

	// 空闲
	this.ready = function() {
		return ready();
	};

	// 其他工作
	this.otherWork = function(reason) {
		return otherWork(reason);
	};

	// 预约
	this.appoint = function(_appointType) {
		return appoint(_appointType);
	};

	// 强制签入
	this.forceLogin = function() {
		return forceLogin();
	};

	// 外呼
	this.makeCall = function(destType, destDesc, callerId) {
		return makeCall(destType, destDesc, callerId);
	};

	// 应答
	this.doAnswer = function() {
		return doAnswer();
	};

	// 挂机
	this.doHangup = function() {
		return doHangup();
	};

	// 结束话路
	this.clearCall = function() {
		return clearCall();
	};

	// 保持
	this.doHold = function() {
		return doHold();
	};

	// 取回
	this.doRetrive = function() {
		return doRetrive();
	};

	// 咨询
	this.doConsulting = function(consultType, destDesc, sessionId) {
		return doConsulting(consultType, destDesc, sessionId);
	};

	// 咨询接回
	this.reconnectCall = function() {
		return reconnectCall();
	};

	// 咨询变转接
	this.forwardCall = function() {
		return forwardCall();
	};

	// 咨询变会议
	this.doMeeting = function() {
		return doMeeting();
	};

	// 振铃转接
	this.deflectCall = function(destType, destDesc) {
		return deflectCall(destType, destDesc);
	};

	// 代接
	this.pickupCall = function(destType, destDesc) {
		return pickupCall(destType, destDesc);
	};

	// 转接
	this.doAdressShift = function() {
		return doAdressShift();
	};

	// 转IVR
	this.doTransferIVR = function(callData) {
		return doTransferIVR(callData);
	};

	//单步 destType 1-会议、2-转接
	this.doSingleStep = function(consultType,destId,destDesc, sessionId) {
		singleStepType = destId;
		return doConsulting(consultType, destDesc, sessionId);
	}

	// 二次拨号
	this.sendDTMF = function(DTMF) {
		return sendDTMF(DTMF);
	};
	//密语 destType (1开始 2结束)
	this.whisper = function(sessionId,destType){

		return whisper(sessionId,destType)
	};
	//座席协作
	this.doAssistService = function(dataKey, dataValue){

		return doAssistService(dataKey, dataValue)
	};

	// 单步转接
	this.singleStepTransfer = function(destType, destDesc) {
		return singleStepTransfer(destType, destDesc);
	};

	// 强制忙碌
	this.forceNotReady = function(agentId) {
		return forceNotReady(agentId);
	};

	// 强制空闲
	this.forceReady = function(agentId) {
		return forceReady(agentId);
	};

	// 强制签出
	this.forceLogout = function(agentId) {
		return forceLogout(agentId);
	};

	// 监听
	this.doListen = function(destDesc) {
		return doListen(destDesc);
	};

	// 强插
	this.doInsert = function() {
		return doInsert();
	};

	// 拦截
	this.interceptCall = function(destType, destDesc) {
		return interceptCall(destType, destDesc);
	};

	// 强拆
	this.doDrop = function() {
		return doDrop();
	};

	// 获取随路数据
	this.getCallData = function(sessionId,dataKey) {
		return getCallData(sessionId,dataKey);
	};

	// 设置随路数据
	this.setCallData = function(sessionId,dataKey, dataValue) {
		return setCallData(sessionId,dataKey, dataValue);
	};

	// 获取坐席状态
	this.getAgentState = function(agentId) {
		return getAgentState(agentId);
	};

	// 开始录音
	this.startRecord = function(fileName) {
		return startRecord(fileName);
	};

	// 结束录音
	this.stopRecord = function() {
		return stopRecord();
	};

	// 抢接路由
	this.routeSelect = function(contactId, destType, destDesc) {
		return routeSelect(contactId, destType, destDesc);
	};

	// 开始静音
	this.startMute = function() {
		return startMute();
	};

	// 结束静音
	this.stopMute = function() {
		return stopMute();
	};

	// 设置SIPHead
	this.setSipHead = function(headKey, headValue) {
		return setSipHead(headKey, headValue);
	};

	// 获取SIPHead
	this.getSipHead = function(headKey, success, error) {
		return getSipHead(headKey, success, error);
	};

	// 坐席媒体控制
	this.execCommand = function(appName, appData, privateData) {
		return execCommand(appName, appData, privateData);
	};
	//坐席列表
	this.getAgentListData = function(){

		return getAgentListData();
	};
	//座席外显号码
	this.getAgentDialCaller = function(){
		return getAgentDialCaller();
	};
	//重置处理
	this.agentStateReset = function(destDesc){
		return agentStateReset(destDesc);
	}

	this.onAgentListData = function(resp){
	};

	this.onAgentDialCallerData = function(resp){
	};

	this.onGetCallData = function(resp){
	};
	this.onWhisperData = function(resp){
	};

	this.onOnline = function(event) {
	};

	this.onOffline = function(event) {
	};

	this.onStatusChanged = function(event) {
	};

	this.onDialing = function(event) {
	};

	this.onOffering = function(event) {
	};

	this.onConnected = function(event) {
	};

	this.onReleased = function(event) {
	};

	this.onRecordEnd = function(event) {
	};

	this.onError = function(event) {
	};

	this.onIMOffering = function(event) {
	};

	this.onIMConnected = function(event) {
	};

	this.onIMReleased = function(event) {
	};

	this.onDevice = function(name, event) {
	};

	this.onEvent = function(event) {
	};
	//获取座席状态
	this.onAgentState = function(event){
	};
};

/*
Copyright (c) 2010,2011,2012,2013,2014 Morgan Roderick http://roderick.dk
License: MIT - http://mrgnrdrck.mit-license.org

https://github.com/mroderick/PubSubJS
*/
(function (root, factory){
    'use strict';

    var PubSub = {};
    root.PubSub = PubSub;

    var define = root.define;

    factory(PubSub);

    // AMD support
    if (typeof define === 'function' && define.amd){
        define(function() { return PubSub; });

        // CommonJS and Node.js module support
    } else if (typeof exports === 'object'){
        if (module !== undefined && module.exports) {
            exports = module.exports = PubSub; // Node.js specific `module.exports`
        }
        exports.PubSub = PubSub; // CommonJS module 1.1.1 spec
        module.exports = exports = PubSub; // CommonJS
    }

}(( typeof window === 'object' && window ) || this, function (PubSub){
    'use strict';

    var messages = {},
        lastUid = -1;

    function hasKeys(obj){
        var key;

        for (key in obj){
            if ( obj.hasOwnProperty(key) ){
                return true;
            }
        }
        return false;
    }

    /**
	 *	Returns a function that throws the passed exception, for use as argument for setTimeout
	 *	@param { Object } ex An Error object
	 */
    function throwException( ex ){
        return function reThrowException(){
            throw ex;
        };
    }

    function callSubscriberWithDelayedExceptions( subscriber, message, data ){
        try {
            subscriber( message, data );
        } catch( ex ){
            setTimeout( throwException( ex ), 0);
        }
    }

    function callSubscriberWithImmediateExceptions( subscriber, message, data ){
        subscriber( message, data );
    }

    function deliverMessage( originalMessage, matchedMessage, data, immediateExceptions ){
        var subscribers = messages[matchedMessage],
            callSubscriber = immediateExceptions ? callSubscriberWithImmediateExceptions : callSubscriberWithDelayedExceptions,
            s;

        if ( !messages.hasOwnProperty( matchedMessage ) ) {
            return;
        }

        for (s in subscribers){
            if ( subscribers.hasOwnProperty(s)){
                callSubscriber( subscribers[s], originalMessage, data );
            }
        }
    }

    function createDeliveryFunction( message, data, immediateExceptions ){
        return function deliverNamespaced(){
            var topic = String( message ),
                position = topic.lastIndexOf( '.' );

            // deliver the message as it is now
            deliverMessage(message, message, data, immediateExceptions);

            // trim the hierarchy and deliver message to each level
            while( position !== -1 ){
                topic = topic.substr( 0, position );
                position = topic.lastIndexOf('.');
                deliverMessage( message, topic, data, immediateExceptions );
            }
        };
    }

    function messageHasSubscribers( message ){
        var topic = String( message ),
            found = Boolean(messages.hasOwnProperty( topic ) && hasKeys(messages[topic])),
            position = topic.lastIndexOf( '.' );

        while ( !found && position !== -1 ){
            topic = topic.substr( 0, position );
            position = topic.lastIndexOf( '.' );
            found = Boolean(messages.hasOwnProperty( topic ) && hasKeys(messages[topic]));
        }

        return found;
    }

    function publish( message, data, sync, immediateExceptions ){
        var deliver = createDeliveryFunction( message, data, immediateExceptions ),
            hasSubscribers = messageHasSubscribers( message );

        if ( !hasSubscribers ){
            return false;
        }

        if ( sync === true ){
            deliver();
        } else {
            setTimeout( deliver, 0 );
        }
        return true;
    }

    /**
	 *	PubSub.publish( message[, data] ) -> Boolean
	 *	- message (String): The message to publish
	 *	- data: The data to pass to subscribers
	 *	Publishes the the message, passing the data to it's subscribers
	**/
    PubSub.publish = function( message, data ){
        return publish( message, data, false, PubSub.immediateExceptions );
    };

    /**
	 *	PubSub.publishSync( message[, data] ) -> Boolean
	 *	- message (String): The message to publish
	 *	- data: The data to pass to subscribers
	 *	Publishes the the message synchronously, passing the data to it's subscribers
	**/
    PubSub.publishSync = function( message, data ){
        return publish( message, data, true, PubSub.immediateExceptions );
    };

    /**
	 *	PubSub.subscribe( message, func ) -> String
	 *	- message (String): The message to subscribe to
	 *	- func (Function): The function to call when a new message is published
	 *	Subscribes the passed function to the passed message. Every returned token is unique and should be stored if
	 *	you need to unsubscribe
	**/
    PubSub.subscribe = function( message, func ){
        if ( typeof func !== 'function'){
            return false;
        }

        // message is not registered yet
        if ( !messages.hasOwnProperty( message ) ){
            messages[message] = {};
        }

        // forcing token as String, to allow for future expansions without breaking usage
        // and allow for easy use as key names for the 'messages' object
        var token = 'uid_' + String(++lastUid);
        messages[message][token] = func;

        // return token for unsubscribing
        return token;
    };

    /* Public: Clears all subscriptions
	 */
    PubSub.clearAllSubscriptions = function clearAllSubscriptions(){
        messages = {};
    };

    /*Public: Clear subscriptions by the topic
	*/
    PubSub.clearSubscriptions = function clearSubscriptions(topic){
        var m;
        for (m in messages){
            if (messages.hasOwnProperty(m) && m.indexOf(topic) === 0){
                delete messages[m];
            }
        }
    };

    /* Public: removes subscriptions.
	 * When passed a token, removes a specific subscription.
	 * When passed a function, removes all subscriptions for that function
	 * When passed a topic, removes all subscriptions for that topic (hierarchy)
	 *
	 * value - A token, function or topic to unsubscribe.
	 *
	 * Examples
	 *
	 *		// Example 1 - unsubscribing with a token
	 *		var token = PubSub.subscribe('mytopic', myFunc);
	 *		PubSub.unsubscribe(token);
	 *
	 *		// Example 2 - unsubscribing with a function
	 *		PubSub.unsubscribe(myFunc);
	 *
	 *		// Example 3 - unsubscribing a topic
	 *		PubSub.unsubscribe('mytopic');
	 */
    PubSub.unsubscribe = function(value){
        var descendantTopicExists = function(topic) {
                var m;
                for ( m in messages ){
                    if ( messages.hasOwnProperty(m) && m.indexOf(topic) === 0 ){
                        // a descendant of the topic exists:
                        return true;
                    }
                }

                return false;
            },
            isTopic    = typeof value === 'string' && ( messages.hasOwnProperty(value) || descendantTopicExists(value) ),
            isToken    = !isTopic && typeof value === 'string',
            isFunction = typeof value === 'function',
            result = false,
            m, message, t;

        if (isTopic){
            PubSub.clearSubscriptions(value);
            return;
        }

        for ( m in messages ){
            if ( messages.hasOwnProperty( m ) ){
                message = messages[m];

                if ( isToken && message[value] ){
                    delete message[value];
                    result = value;
                    // tokens are unique, so we can just stop here
                    break;
                }

                if (isFunction) {
                    for ( t in message ){
                        if (message.hasOwnProperty(t) && message[t] === value){
                            delete message[t];
                            result = true;
                        }
                    }
                }
            }
        }

        return result;
    };
}));
